/*
 * @copyright: Huang Ding
 * @Author: ding-cx
 * @Date: 2020-12-29 08:44:29
 * @LastEditors: ding-cx
 * @LastEditTime: 2021-10-30 12:17:54
 * @Description: file content
 */

import { Context, EggAppConfig, IResponseBodyData } from "egg";

/**
 *中间件，使用jwt验证请求是否登录。
 *
 * @param {*} options
 * @return {*}
 */
export default (options: EggAppConfig["jwtLogin"]): any => {
	return async function jwtLogin(ctx: Context, next: () => Promise<any>): Promise<void> {
		if (!options.enable) {
			return next();
		}
		const token =
			ctx.get("x-authorization") ||
			ctx.get("authorization") ||
			ctx.query?.access_token ||
			ctx.request.query?.access_token ||
			ctx.cookies.get("x-access-token") ||
			ctx.session?.access_token;

		const res: IResponseBodyData = {
			data: null,
			msg: "",
			code: 50000,
			details: [],
		};
		//根据配置确定是否是全局匹配。若全局匹配则判断白名单。
		if (options.isGlobal) {
			// 判断路由白名单，如果在，则不进行jwt token check
			if (isInWhiteList(ctx, options.routeWhiteList)) {
				await next();
				return;
			}
		}

		if (!token) {
			const msg = ctx.__("can not found token on header or query.");
			ctx.status = 401;
			ctx.logger.warn(msg);
			res.msg = msg;
			res.code = 50001; // 401 和 50001 共同标记jwt无效，前端可识别处理导航到登录页等需求。
			ctx.body = res;
			return;
		}

		const checkResult = verifyJwt(ctx, token as string);
		if (!checkResult) {
			ctx.status = 401;
			res.code = 50001;
			res.msg = ctx.gettext(`jwt verify error or the token was expired.`);
			ctx.body = res;
			return;
		}
		await next();
	};
};

function verifyJwt(ctx: Context, token: string) {
	token = token.replace(/^Bearer\s/, "");
	try {
		// 解码token
		let decode = ctx.app.jwt.verify(token as string, ctx.app.config.jwt.secret) as any as Context["loginUserInfo"];
		// ctx.logger.debug("[jwtLogin middleware] token check ok.\r\n", decode);
		ctx.loginUserInfo = decode;
		ctx.jwtToken = token;
		return true;
	} catch (error:any) {
		ctx.logger.error(`token验证失败或已过期！%s`, error.message);
		return false;
	}
}

/**
 *判断是否在白名单中
 *
 * @param {object} ctx egg's context object.
 * @return {*}
 */
function isInWhiteList(ctx: Context, route_white_list: string[] = []) {
	// console.log(ctx.request.path);
	const list: string[] = route_white_list || [];
	const { path } = ctx.request;
	return list.some((item) => path.substring(0, item.length) === item);
}
